/// 使用Rust的标准库中的u32::from_str_radix()可以实现。
/// 这里没有使用标准库函数，使用手动建立进制表来实现。
pub fn convert_base(num_str: &str, to_base: u32) -> String {
    let value = num_str_to_value(num_str);
    to_base_string(value, to_base)
}

// --------------------------------------------------------------------------
// 把`num_str`转换成10进制的值
fn num_str_to_value(num_str: &str) -> usize {
    let mut count = 0usize;
    let (value_str, base) = get_base_value(num_str); // 获取值字符串和进制
    for (i, ch) in value_str.chars().into_iter().rev().enumerate() {
        let ch = ch.to_ascii_uppercase(); // 首选把字符转换成大写

        // 判断一下字符的范围
        let value = if (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') {
            BASES[base].get_value(ch)
        } else {
            panic!("值输入有误");
        };
        count += value * base.pow(i as u32);
    }
    count
}

// 把数值转成目标进制
fn to_base_string(mut value: usize, to_base: u32) -> String {
    let mut arr = Vec::new();
    loop {
        let quotient = value / to_base as usize;
        let remainder = value % to_base as usize;
        arr.push(
            BASES[to_base as usize]
                .get_char(remainder)
                .to_ascii_lowercase() as u8,
        );
        if quotient == 0 {
            break;
        }
        value = quotient;
    }
    arr.reverse(); // 最后的字符串要反序一下
    String::from_utf8(arr).expect("转换失败")
}

// 利用括号进行分割，获取到`num_str`的值和进制的部分
// # PANIC: 如果`num_str`不是`9(10)`这样的格式，会引发panic.
fn get_base_value(num_str: &str) -> (&str, usize) {
    let arr = num_str.split("(").collect::<Vec<_>>(); // 先分割"("
    let value_str = arr[0]; // 获取"("前面的字符串
    let base = arr[1].split(")").collect::<Vec<_>>()[0]; // 再对arr[1]分割,获取")"前面进制字符串
    (value_str.trim(), base.parse().expect("进制应该是一个数值"))
}

// --------------------------------------------------------------------------
// 所有进制的表，进制的值就是这个数组的索引，快速定位到需要的进制
const BASES: [&dyn GetValue; 17] = [
    &TWO as &dyn GetValue,
    &TWO as &dyn GetValue, // 这两个是用来补位的，索引0-1是没用的，2-16才是我们需要的进制
    &TWO as &dyn GetValue,
    &THREE as &dyn GetValue,
    &FOUR as &dyn GetValue,
    &FIVE as &dyn GetValue,
    &SIX as &dyn GetValue,
    &SEVEN as &dyn GetValue,
    &EIGHT as &dyn GetValue,
    &NINE as &dyn GetValue,
    &TEN as &dyn GetValue,
    &ELEVEN as &dyn GetValue,
    &TWELVE as &dyn GetValue,
    &THIRTEEN as &dyn GetValue,
    &FOURTEEN as &dyn GetValue,
    &FIFTEEN as &dyn GetValue,
    &SIXTEEN as &dyn GetValue,
];

// --------------------------------------------------------------------------
// 获取`ch`对应的值
trait GetValue {
    fn get_value(&self, ch: char) -> usize;
    fn get_char(&self, value: usize) -> char;
}

// 通过下标获取字符对应的值
macro_rules! impl_get_value {
    ($base:expr) => {
        impl GetValue for [char; $base] {
            fn get_value(&self, ch: char) -> usize {
                for (i, &v) in self.into_iter().enumerate() {
                    if v == ch {
                        return i;
                    }
                }
                panic!("不是正确的进制字符");
            }

            fn get_char(&self, value: usize) -> char {
                self[value]
            }
        }
    };
}

impl_get_value!(2);
impl_get_value!(3);
impl_get_value!(4);
impl_get_value!(5);
impl_get_value!(6);
impl_get_value!(7);
impl_get_value!(8);
impl_get_value!(9);
impl_get_value!(10);
impl_get_value!(11);
impl_get_value!(12);
impl_get_value!(13);
impl_get_value!(14);
impl_get_value!(15);
impl_get_value!(16);

// 定义进制2-16所有的表示
const TWO: [char; 2] = ['0', '1'];
const THREE: [char; 3] = ['0', '1', '2'];
const FOUR: [char; 4] = ['0', '1', '2', '3'];
const FIVE: [char; 5] = ['0', '1', '2', '3', '4'];
const SIX: [char; 6] = ['0', '1', '2', '3', '4', '5'];
const SEVEN: [char; 7] = ['0', '1', '2', '3', '4', '5', '6'];
const EIGHT: [char; 8] = ['0', '1', '2', '3', '4', '5', '6', '7'];
const NINE: [char; 9] = ['0', '1', '2', '3', '4', '5', '6', '7', '8'];
const TEN: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const ELEVEN: [char; 11] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A'];
const TWELVE: [char; 12] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'];
const THIRTEEN: [char; 13] = [
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
];
const FOURTEEN: [char; 14] = [
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
];
const FIFTEEN: [char; 15] = [
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
];
const SIXTEEN: [char; 16] = [
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
];
// --------------------------------------------------------------------------
